﻿using UnityEngine;
using System.Collections.Generic;

namespace TTUnityPlugin {
/// <summary>
/// A generic class for storing a pool of objects.
/// </summary>
/// <typeparam name="T">A type with a default constructor.</typeparam>
internal class LTObjectPool<T> where T : new() {
    /// <summary>
    /// The stack.
    /// </summary>
    private readonly Stack<T> m_Stack = new Stack<T>();
    /// <summary>
    /// Function to gen new Object
    /// </summary>
    private readonly System.Func<object, object, object, T> m_FunNew;
    /// <summary>
    /// Action to perform when getting an available item from the pool.
    /// </summary>
    private readonly System.Action<T> m_ActionOnGet;
    /// <summary>
    /// Action to perform when releasing an item to the pool.
    /// </summary>
    private readonly System.Action<T> m_ActionOnRelease;

    /// <summary>
    /// Gets the total number of items in the pool.
    /// </summary>
    /// <value>The total number of items in the pool.</value>
    public int Count { get; private set; }
    /// <summary>
    /// Gets the number of items currently in use.
    /// </summary>
    /// <value>The number of items currently in use.</value>
    public int CountActive { get { return this.Count - this.CountInactive; } }
    /// <summary>
    /// Gets the number of items currently available for use.
    /// </summary>
    /// <value>The number of items currently available for use.</value>
    public int CountInactive { get { return m_Stack.Count; } }

    /// <summary>
    /// Initializes a new instance of the <see cref="Candlelight.ObjectPool{T}"/> class.
    /// </summary>
    /// <param name="actionOnGet">Action to perform when getting an item.</param>
    /// <param name="actionOnRelease">Action to perform when releasing an item.</param>
    public LTObjectPool(System.Action<T> actionOnGet, System.Action<T> actionOnRelease, System.Func<object, object, object, T> funNew = null) {
        m_FunNew = funNew;
        m_ActionOnGet = actionOnGet;
        m_ActionOnRelease = actionOnRelease;
    }

    /// <summary>
    /// Gets an available item from the pool.
    /// </summary>
    public T Get(object p1 = null, object p2 = null, object p3 = null) {
        T element;
        if(m_Stack.Count == 0) {
            element = m_FunNew == null ? new T() : m_FunNew(p1, p2, p3);
            ++this.Count;
        } else {
            element = m_Stack.Pop();
        }
        if(m_ActionOnGet != null) {
            m_ActionOnGet(element);
        }
        return element;
    }

    /// <summary>
    /// Releases an item to the pool.
    /// </summary>
    /// <param name="element">Element to release.</param>
    public void Release(T element) {
        if(m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element)) {
            Debug.LogError("Internal error. Trying to destroy object that is already released to pool.");
        }
        if(m_ActionOnRelease != null) {
            m_ActionOnRelease(element);
        }
        m_Stack.Push(element);
    }
}

}
